package com.io.netty.dubborpc.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.lang.reflect.Proxy;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class NettyClient {

    //创建线程池，线程池中线程的个数是跟本机器的cpu的核数相关联的
    //好像是四个，实验结果是只有四个线程轮流执行call方法
    private static ExecutorService executor =
            Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    private static NettyClientHandler client;
    private int count = 0;

    //(HelloService .class, providerName)
    //编写方法使用代理模式
    public Object getBean(final Class<?> serivceClass, final String providerName) {

        //获得当前线程的类加载器：Thread.currentThread().getContextClassLoader()
        //要被代理的对象：new Class<?>[]{serivceClass}
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class<?>[]{serivceClass}, (proxy, method, args) -> {

                    System.out.println("(proxy, method, args) 进入...." + (++count) + " 次");

                    //判断客户端是否已经被初始化，如果为空，就代表没有被初始化，就需要初始化
                    //{}  部分的代码，客户端每调用一次 hello, 就会进入到该代码
                    if (client == null) {
                        initClient();
                    }

                    //设置要发给服务器端的信息
                    //providerName 协议头 args[0] 就是客户端调用api hello(???), 参数
                    client.setPara(providerName + args[0]);
                    //把client提交到线程池中，这个会调用handler中的call方法
                    //好像是因为那个NettyClientHandler实现了Callable接口的原因
                    //值得注意的是call方法是由executor线程池中的四个线程轮流执行的
                    return executor.submit(client).get();
                });
    }

    //初始化客户端
    private static void initClient(){
        client = new NettyClientHandler();
        //创建EventLoopGroup
        NioEventLoopGroup group = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        try {
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(
                            new ChannelInitializer<SocketChannel>() {
                                protected void initChannel(SocketChannel ch) throws Exception {
                                    ChannelPipeline pipeline = ch.pipeline();
                                    pipeline.addLast(new StringDecoder());
                                    pipeline.addLast(new StringEncoder());
                                    pipeline.addLast(client);
                                }
                            }
                    );
            //当前线程并会阻塞在这，如果连接成功则会继续往下执行
            //这个连接是由netty创建的那个bossGroup线程来维护
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 7000).sync();
            //这个程序中不能把这个同步写上，不然主线程就一直同步在这等待关闭，数据就无法发送了
            //channelFuture.channel().closeFuture().sync();
        }catch (Exception e){
           e.printStackTrace();
        }
        /*finally {
            //一旦bootstrap.connect("127.0.0.1", 7000).sync()连接上之后
            //主线程就会执行group.shutdownGracefully()，将group关闭
              //关闭之后交互就无法进行了，所以除非交互完成，不然不能关闭
            group.shutdownGracefully();
        }*/
    }

}
